package com.ljh.aop.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author: Themberfue
 * @date: 2025/3/25 19:30
 * @description:
 */
@Slf4j
@Aspect
@Component
@Order(6)
public class AspectDemo1 { // ! AOP (Aspect Oriented Programing，面向切面编程)，即为一种思想，Spring AOP 则是对该思想的一种实现
    // * 将切点表达式进行封装，以实现复用的效果
    @Pointcut("execution(* com.ljh.aop.controller.*.*(..))")
    public void pt() {}

    // ? PointCut ("切点")
    // ? @Around 等下面类似的注解，表示告知该方法的具体执行时机
    // ? @Around 为环绕通知，此注解标识的通知方法在目标方法执行前、后都被执行
    @Around("pt()") // * @Around 括号里的为 "切点表达式"，表示对哪些方法生效
    public Object aspectDemo(ProceedingJoinPoint pjp) {
        // ? 该通知方法内部的逻辑统称为 "通知"
        // TODO 执行目标方法之前执行
        log.info("demo1：目标方法执行前...");
        Object res;
        try {
            // TODO 执行目标方法
            res = pjp.proceed(); // ? proceed 会执行真正的方法，也就是目标方法，该目标也被称为 "连接点"
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        // TODO 执行目标方法之后执行
        log.info("demo1：目标方法执行后...");
        return res;
    }
    // ! 切点 + 通知 = 切面

    // ! @Around 自身就可以做到 @Before、@After 以及 @AfterReturning 的效果
    // ! 所以一般不需要再单独配置 @Before、@After 以及 @AfterReturning

    // ? @Before 为前置通知，此注解标识的通知方法在目标方法执行前被执行
    @Before("pt()")
    public void doBefore() {
        log.info("doBefore...");
    }

    // ? @After 为后置通知，此注解标识的通知方法在目标方法执行后被执行，无论是否有异常都会被执行
    @After("pt()")
    public void doAfter() {
        log.info("doAfter...");
    }

    // ? @AfterReturning 为返回后通知，此注解标识的通知方法在目标方法执行后被执行，如果有异常则不执行
    @AfterReturning("pt()")
    public void doAfterReturning() {
        log.info("doAfterReturning...");
    }

    // ? @AfterThrowing 为异常后通知，此注解标识的通知方法在目标方法发生异常后被执行
    @AfterThrowing("pt()")
    public void doAfterThrowing() {
        log.info("doAfterThrowing...");
    }
}
